package com.fgba.express.data.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fgba.express.comm.GlobalConst;
import com.fgba.express.comm.enums.State;
import com.fgba.express.core.service.impl.CrudServiceImpl;
import com.fgba.express.data.dao.IUserDao;
import com.fgba.express.data.dto.MenuDto;
import com.fgba.express.data.dto.UserDto;
import com.fgba.express.data.entity.*;
import com.fgba.express.data.mapstruct.UserStruct;
import com.fgba.express.data.search.UserSearch;
import com.fgba.express.data.service.*;
import com.fgba.express.util.BeanContextUtil;
import com.fgba.express.util.NullChecker;
import com.fgba.express.util.RedisUtil;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户 服务实现类
 * </p>
 *
 * @author fgba
 * @since 2022-02-24
 */
@Service
public class UserServiceImpl extends CrudServiceImpl<IUserDao, User, UserDto, UserSearch, UserStruct> implements IUserService {

    @Resource
    RedisUtil redisUtil;

    @Override
    public User getUserByUsername(String username) {
        return baseMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getUsername, username));
    }

    @Override
    public User getUserByWxId(String wxId) {
        return baseMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getWxId, wxId));
    }

    @Override
    public boolean bindUser(String username, String password, String openId) {
        User user = new User();
        user.setWxId(openId);
        if (getOne(Wrappers.<User>lambdaQuery().eq(User::getWxId, openId)) == null) {
            username = username.trim();
            return false;
        }
        return true;
    }

    @Override
    public String getUserAuthorityInfo(Long userId) {
        User user = getById(userId);
        if (NullChecker.isNull(user)) {
            return null;
        }
        StringBuffer authority = new StringBuffer();
        if (redisUtil.hasKey(GlobalConst.GRANTED_AUTHORITY + user.getUsername())) {
            authority.append((String) redisUtil.get(GlobalConst.GRANTED_AUTHORITY + user.getUsername()));
        } else {
            LambdaQueryWrapper<UserRoleMapping> userRoleMappingLambdaQueryWrapper = Wrappers.<UserRoleMapping>lambdaQuery().select(UserRoleMapping::getRoleId).eq(UserRoleMapping::getUserId, userId);
            List<Long> roleIds = BeanContextUtil.getBean(IUserRoleMappingService.class).list(userRoleMappingLambdaQueryWrapper).stream().map(UserRoleMapping::getRoleId).collect(Collectors.toList());
            List<Role> roles = BeanContextUtil.getBean(IRoleService.class).list(Wrappers.<Role>lambdaQuery().in(Role::getId, roleIds));
            if (NullChecker.isNull(roles)) {
                return null;
            }
            authority.append(
                    roles.stream()
                            .map(role -> "ROLE_" + role.getCode()).collect(Collectors.joining(",")));
            authority.append(",");
            IMenuService menuService = BeanContextUtil.getBean(IMenuService.class);
            List<Long> menuIds = menuService.getNavMenus(userId).stream().map(MenuDto::getId).collect(Collectors.toList());
            if (menuIds.size() > 0) {
                List<Menu> menus = menuService.listByIds(menuIds);
                authority.append(
                        menus.stream()
                                .map(Menu::getPerms).collect(Collectors.joining(",")));
            }
            log.info("用户ID - {} ---拥有的权限：{}", userId, authority);
            redisUtil.set(GlobalConst.GRANTED_AUTHORITY + user.getUsername(), authority.toString(), 60 * 60);
        }
        return authority.toString();
    }

    @Override
    public void clearUserAuthorityInfo(String username) {
        redisUtil.del(GlobalConst.GRANTED_AUTHORITY + username);
    }

    @Override
    public void clearUserAuthorityInfoByRoleId(Long roleId) {
        List<UserRoleMapping> userRoleMappings = BeanContextUtil.getBean(IUserRoleMappingService.class).list(Wrappers.<UserRoleMapping>lambdaQuery().eq(UserRoleMapping::getRoleId, roleId));
        if (NullChecker.isNull(userRoleMappings)) {
            return;
        }
        List<Long> userIds = userRoleMappings.stream().map(UserRoleMapping::getUserId).collect(Collectors.toList());
        List<User> users = this.list(new QueryWrapper<User>().lambda().in(User::getId, userIds));
        users.forEach(user -> {
            this.clearUserAuthorityInfo(user.getUsername());
        });
    }

    @Override
    public void clearUserAuthorityInfoByMenuId(Long menuId) {
        if (NullChecker.isNullOrZero(menuId)) {
            return;
        }
        List<RoleMenuMapping> roleMenuMappings = BeanContextUtil.getBean(IRoleMenuMappingService.class).list(Wrappers.<RoleMenuMapping>lambdaQuery().eq(RoleMenuMapping::getMenuId, menuId));
        if (NullChecker.isNull(roleMenuMappings)) {
            return;
        }
        List<Long> roleIds = roleMenuMappings.stream().map(RoleMenuMapping::getRoleId).collect(Collectors.toList());
        List<UserRoleMapping> userRoleMappings = BeanContextUtil.getBean(IUserRoleMappingService.class).list(Wrappers.<UserRoleMapping>lambdaQuery().in(UserRoleMapping::getRoleId, roleIds));
        List<User> users = this.listByIds(userRoleMappings.stream().map(UserRoleMapping::getUserId).collect(Collectors.toList()));
        users.forEach(user -> {
            this.clearUserAuthorityInfo(user.getUsername());
        });
    }

    @Override
    public boolean removeById(Serializable id) {
        User user = baseMapper.selectById(id);
        user.setState(State.STATUS_AVAILABLE);
        user.setModifiedTime(LocalDateTime.now());
        return baseMapper.updateById(user) > 0;
    }

    @Override
    public boolean removeByIds(Collection<? extends Serializable> idList) {
        List<User> users = baseMapper.selectBatchIds(idList);
        return updateBatchById(users);
    }
}
